//      svm.h
//      
//      Copyright 2012 tqlong <tqlong@espada>
//      
//      This program is free software; you can redistribute it and/or modify
//      it under the terms of the GNU General Public License as published by
//      the Free Software Foundation; either version 2 of the License, or
//      (at your option) any later version.
//      
//      This program is distributed in the hope that it will be useful,
//      but WITHOUT ANY WARRANTY; without even the implied warranty of
//      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//      GNU General Public License for more details.
//      
//      You should have received a copy of the GNU General Public License
//      along with this program; if not, write to the Free Software
//      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
//      MA 02110-1301, USA.

#ifndef __SUPPORT_VECTOR_MACHINES_H
#define __SUPPORT_VECTOR_MACHINES_H

#include "stl-helper.h"
#include "data.h"

class KernelCache
{
 protected:
  int size_;
  std::map<int, arma::vec> c_;
  arma::vec diag_;
  std::vector<long long> time_stamp_;
  long long now_;
 public:
  KernelCache(int data_size, boost::program_options::variables_map& vm);
  void add(int i, const arma::vec& k);
  bool contains(int i, int j);
  double kval(int i, int j);
  void set_diag(const arma::vec& diag) { diag_ = diag; }
};


class Kernel
{
 public:
  typedef MatrixData Data;
  enum KERNEL_TYPE { LINEAR = 0, GAUSSIAN };
 protected:
  Data& data_;
  boost::program_options::variables_map& vm_;
  KERNEL_TYPE type_;
  KernelCache cache_;
  double (*kernel_function_)(const arma::vec& x, const arma::vec& y);
 public:
  Kernel(MatrixData& data, boost::program_options::variables_map& vm);

  double operator() (int i, int j); // K(x_i, x_j)
  double operator() (int i, const arma::vec& x); // K(x_i, x)
  arma::vec operator() (int j); // column K(x_i, x_j) i = 1..n
  arma::vec operator() (const arma::vec& x); // column K(x_i, x) i = 1..n

 protected:
  
  static double linear_kernel(const arma::vec& x, const arma::vec& y);
  static double gaussian_kernel(const arma::vec& x, const arma::vec& y);
};

class KernelL1SVM
{
 public:
  typedef MatrixData Data;
  typedef struct {
    arma::vec alpha;
    double bias;
    vi_t sp; // support vector indices
  } Param;
  typedef arma::vec Output;
 protected:
  Data& data_;
  boost::program_options::variables_map& vm_;
  Kernel& K_;
  Param p_;
  bool use_bias_;
  double C_;

  arma::vec g_; // gradient of dual variables (alpha)
 public:
  KernelL1SVM(MatrixData& data, boost::program_options::variables_map& vm, Kernel& K) ;
	
  void solve(bool use_bias = true) ;
  Output output(Data& test) const;
  double threshold() const { return 0.5; }
	
  Param get_param() const ;
  void set_param(const Param& w);	
	
  void print(const std::string& outputFile) const;
	
  static void print_stats(std::ostream& output, const ClassPerf& cp);
};

#endif
